package org.grc.app.z_project.z_framework.mvc;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.grc.app.z_project.z_framework.ConfigHelper;
import org.grc.app.z_project.z_framework.FrameworkConstant;
import org.grc.app.z_project.z_framework.HelperLoader;
import org.grc.app.z_project.z_framework.bean.Data;
import org.grc.app.z_project.z_framework.bean.Handler;
import org.grc.app.z_project.z_framework.bean.Param;
import org.grc.app.z_project.z_framework.bean.View;
import org.grc.app.z_project.z_framework.helper.BeanHelper;
import org.grc.app.z_project.z_framework.helper.ControllerHelper;
import org.grc.app.z_project.z_framework.util.ArrayUtil;
import org.grc.app.z_project.z_framework.util.CodecUtil;
import org.grc.app.z_project.z_framework.util.JsonUtil;
import org.grc.app.z_project.z_framework.util.ReflectionUtil;
import org.grc.app.z_project.z_framework.util.StreamUtil;
import org.grc.app.z_project.z_framework.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 请求转发器（MCV框架核心）
 * 
 * @author grc
 * @since 1.0-SNAPSHOT
 */
@WebServlet(urlPatterns="/*", loadOnStartup=0)
public class DispatcherServlet extends HttpServlet{

	// serial version uid
	private static final long serialVersionUID = 1L;
	
	private static final Logger LOGGER = LoggerFactory.getLogger(DispatcherServlet.class);

	@Override
	public void init(ServletConfig servletConfig) throws ServletException {
		// 初始化相关 Helper类
		HelperLoader.init();
		// 获取 ServletContext对象（用于注册 Servlet）
		ServletContext servletContext = servletConfig.getServletContext();
		// 注册处理 JSP的Servlet
		ServletRegistration jspServlet = servletContext.getServletRegistration("jsp");
		jspServlet.addMapping(ConfigHelper.getAppJspPath());
		// 注册处理静态资源的默认 Servlet
		ServletRegistration defaultServlet = servletContext.getServletRegistration("default");
		defaultServlet.addMapping(ConfigHelper.getAppAssetPath() + "*");
		LOGGER.info("DispatcherServlet init successfully");
	}
	
	@Override
	public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		LOGGER.debug("DispatcherServlet do service()");
		LOGGER.debug("request.getServletPath() " + request.getServletPath());
		LOGGER.debug("request.getPathInfo() " + request.getPathInfo());
		ServletHelper.init(request, response);
		try {
			// 获取请求方法与请求路径
			String requestMethod = request.getMethod().toLowerCase();// 请求方式，为get，post，delete等
			String requestPath = request.getPathInfo();
			// 获取 Action处理器
			Handler handler = ControllerHelper.getHandler(requestMethod, requestPath);
			if (handler != null) {
				// 获取 Controller类及其 Bean实例
				Class<?> controllerClass = handler.getControllerClass();
				Object controllerBean = BeanHelper.getBean(controllerClass);
				// 创建请求参数对象
				Map<String, Object> paramMap = new HashMap<String, Object>();
				Enumeration<String> paramNames = request.getParameterNames();
				while (paramNames.hasMoreElements()) {
					String paramName = paramNames.nextElement();
					String paramValue = request.getParameter(paramName);
					paramMap.put(paramName, paramValue);
				}
				String body = CodecUtil.decodeURL(StreamUtil.getString(request.getInputStream()));
				if (StringUtil.isNotEmpty(body)) {
					String[] params = StringUtil.splitString(body, "&");
					if (ArrayUtil.isNotEmpty(params)) {
						for (String param : params) {
							String[] array = StringUtil.splitString(param, "=");
							if (ArrayUtil.isNotEmpty(array) && array.length == 2) {
								String paramName = array[0];
								String paramValue = array[1];
								paramMap.put(paramName, paramValue);
							}
						}
					}
				}
				
				Param param = new Param(paramMap);
				// 调用 Action方法
				Method actionMethod = handler.getActionMethod();
				Object result = null;
				if (param.isEmpty()) {
					result = ReflectionUtil.invokeMethod(controllerBean, actionMethod);
				} else {
					result = ReflectionUtil.invokeMethod(controllerBean, actionMethod, param);
				}
				// 处理 Action方法返回值
				if (result instanceof View) {
					// 返回 JSP
					View view = (View) result;
					String path = view.getPath();
					// "/"开头的根路径开头就重定向 
					/*
					 * response.sendRedirect(url)跳转到指定的URL地址后，上个页面（跳转之前的原来页面）中的请求全部结束，
					 * 原request对象将会消亡，数据将会消失。紧接着，当前新页面会新建request对象，即产生新的request对象。
					 * 【详细过程：redirect 会首先发一个response给浏览器，然后浏览器收到这个response后再发一个requeset给服务器，
					 * 服务器接收后发新的response给浏览器。这时页面从浏览器获取来的是一个新的request。
					 * 这时，在原来跳转之前的页面用request.setAttribute存的东西都没了，如果在当前的新页面中用request.getAttribute取，
					 * 得到的将会是null。】
					 * 
					 * request.getRequestDispatcher(url).forward(request,response)是采用请求转发方式，
					 * 在跳转页面的时候是带着原来页面的request和response跳转的，request对象始终存在，不会重新创建。
					 * 【详细过程：forward 发生在服务器内部, 是在浏览器完全不知情的情况下发给了浏览器另外一个页面的response. 
					 * 这时页面收到的request不是从浏览器直接发来的，可能是在转页时己经用request.setAttribute在request里放了数据，
					 * 在转到的页面就可以直接用request.getAttribute获得数据了。】
					 */
					if (path.startsWith("/")) {
						response.sendRedirect(request.getContextPath() + path);
					} else {
						Map<String, Object> model = view.getModel();
						for (Map.Entry<String, Object> entry : model.entrySet()) {
							request.setAttribute(entry.getKey(), entry.getValue());
						}
						request.getRequestDispatcher(ConfigHelper.getAppJspPath() + path).forward(request, response);
					}
				} else if (result instanceof Data) {
					// 返回 JSON数据
					Data data = (Data) result;
					Object model = data.getModel();
					if (model != null) {
						response.setContentType(FrameworkConstant.RESPONSE_CONTENTTYPE_JSON);
						response.setCharacterEncoding(FrameworkConstant.UTF_8);
						PrintWriter printWriter = response.getWriter();
						String json = JsonUtil.toJSON(model);
						printWriter.write(json);
						printWriter.flush();
						printWriter.close();
					}
				}
			}
		} finally {
			ServletHelper.destroy();
		}
	}
}
